{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# IP Adapter Negative Noise\n",
    "\n",
    "Diffusers pipelines are fully integrated with IP-Adapter, which allows you to prompt the diffusion model with an image. However, it does not support negative image prompts (there is no negative_ip_adapter_image argument) the same way it supports negative text prompts. When you pass an ip_adapter_image, it will create a zero-filled tensor as a negative image. This script shows you how to create a negative noise from ip_adapter_image and use it to significantly improve the generation quality while preserving the composition of images.\n",
    "\n",
    "[cubiq](https://github.com/cubiq) initially developed this feature in his [repository](https://github.com/cubiq/ComfyUI_IPAdapter_plus). The community script was contributed by [asomoza](https://github.com/Somoza). You can find more details about this experimentation [this discussion](https://github.com/huggingface/diffusers/discussions/7167)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Requirement already satisfied: diffusers in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (0.31.0)\n",
      "Requirement already satisfied: torch in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (2.2.1+cu121)\n",
      "Requirement already satisfied: accelerate in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (1.1.1)\n",
      "Requirement already satisfied: transformers in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (4.46.2)\n",
      "Requirement already satisfied: importlib-metadata in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from diffusers) (8.5.0)\n",
      "Requirement already satisfied: filelock in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from diffusers) (3.16.1)\n",
      "Requirement already satisfied: huggingface-hub>=0.23.2 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from diffusers) (0.26.2)\n",
      "Requirement already satisfied: numpy in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from diffusers) (1.26.4)\n",
      "Requirement already satisfied: regex!=2019.12.17 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from diffusers) (2024.11.6)\n",
      "Requirement already satisfied: requests in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from diffusers) (2.32.3)\n",
      "Requirement already satisfied: safetensors>=0.3.1 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from diffusers) (0.4.5)\n",
      "Requirement already satisfied: Pillow in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from diffusers) (11.0.0)\n",
      "Requirement already satisfied: typing-extensions>=4.8.0 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from torch) (4.12.2)\n",
      "Requirement already satisfied: sympy in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from torch) (1.13.3)\n",
      "Requirement already satisfied: networkx in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from torch) (3.4.2)\n",
      "Requirement already satisfied: jinja2 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from torch) (3.1.4)\n",
      "Requirement already satisfied: fsspec in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from torch) (2024.10.0)\n",
      "Requirement already satisfied: nvidia-cuda-nvrtc-cu12==12.1.105 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from torch) (12.1.105)\n",
      "Requirement already satisfied: nvidia-cuda-runtime-cu12==12.1.105 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from torch) (12.1.105)\n",
      "Requirement already satisfied: nvidia-cuda-cupti-cu12==12.1.105 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from torch) (12.1.105)\n",
      "Requirement already satisfied: nvidia-cudnn-cu12==8.9.2.26 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from torch) (8.9.2.26)\n",
      "Requirement already satisfied: nvidia-cublas-cu12==12.1.3.1 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from torch) (12.1.3.1)\n",
      "Requirement already satisfied: nvidia-cufft-cu12==11.0.2.54 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from torch) (11.0.2.54)\n",
      "Requirement already satisfied: nvidia-curand-cu12==10.3.2.106 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from torch) (10.3.2.106)\n",
      "Requirement already satisfied: nvidia-cusolver-cu12==11.4.5.107 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from torch) (11.4.5.107)\n",
      "Requirement already satisfied: nvidia-cusparse-cu12==12.1.0.106 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from torch) (12.1.0.106)\n",
      "Requirement already satisfied: nvidia-nccl-cu12==2.19.3 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from torch) (2.19.3)\n",
      "Requirement already satisfied: nvidia-nvtx-cu12==12.1.105 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from torch) (12.1.105)\n",
      "Requirement already satisfied: triton==2.2.0 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from torch) (2.2.0)\n",
      "Requirement already satisfied: nvidia-nvjitlink-cu12 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from nvidia-cusolver-cu12==11.4.5.107->torch) (12.6.77)\n",
      "Requirement already satisfied: packaging>=20.0 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from accelerate) (24.1)\n",
      "Requirement already satisfied: psutil in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from accelerate) (6.1.0)\n",
      "Requirement already satisfied: pyyaml in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from accelerate) (6.0.2)\n",
      "Requirement already satisfied: tokenizers<0.21,>=0.20 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from transformers) (0.20.3)\n",
      "Requirement already satisfied: tqdm>=4.27 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from transformers) (4.66.6)\n",
      "Requirement already satisfied: zipp>=3.20 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from importlib-metadata->diffusers) (3.21.0)\n",
      "Requirement already satisfied: MarkupSafe>=2.0 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from jinja2->torch) (3.0.2)\n",
      "Requirement already satisfied: charset-normalizer<4,>=2 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from requests->diffusers) (3.4.0)\n",
      "Requirement already satisfied: idna<4,>=2.5 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from requests->diffusers) (3.10)\n",
      "Requirement already satisfied: urllib3<3,>=1.21.1 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from requests->diffusers) (2.2.3)\n",
      "Requirement already satisfied: certifi>=2017.4.17 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from requests->diffusers) (2024.8.30)\n",
      "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /system/conda/miniconda3/envs/cloudspace/lib/python3.10/site-packages (from sympy->torch) (1.3.0)\n",
      "Note: you may need to restart the kernel to use updated packages.\n"
     ]
    }
   ],
   "source": [
    "pip install diffusers torch accelerate transformers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "The cache for model files in Transformers v4.22.0 has been updated. Migrating your old cache. This is a one-time only operation. You can interrupt this and resume the migration later on by calling `transformers.utils.move_cache()`.\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "8ed53bd6790c44ee8e75b52486e011f8",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "0it [00:00, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import torch\n",
    "\n",
    "from diffusers import AutoencoderKL, DPMSolverMultistepScheduler, StableDiffusionXLPipeline\n",
    "from diffusers.models import ImageProjection\n",
    "from diffusers.utils import load_image\n",
    "\n",
    "\n",
    "def encode_image(\n",
    "    image_encoder,\n",
    "    feature_extractor,\n",
    "    image,\n",
    "    device,\n",
    "    num_images_per_prompt,\n",
    "    output_hidden_states=None,\n",
    "    negative_image=None,\n",
    "):\n",
    "    dtype = next(image_encoder.parameters()).dtype\n",
    "\n",
    "    if not isinstance(image, torch.Tensor):\n",
    "        image = feature_extractor(image, return_tensors=\"pt\").pixel_values\n",
    "\n",
    "    image = image.to(device=device, dtype=dtype)\n",
    "    if output_hidden_states:\n",
    "        image_enc_hidden_states = image_encoder(image, output_hidden_states=True).hidden_states[-2]\n",
    "        image_enc_hidden_states = image_enc_hidden_states.repeat_interleave(num_images_per_prompt, dim=0)\n",
    "\n",
    "        if negative_image is None:\n",
    "            uncond_image_enc_hidden_states = image_encoder(\n",
    "                torch.zeros_like(image), output_hidden_states=True\n",
    "            ).hidden_states[-2]\n",
    "        else:\n",
    "            if not isinstance(negative_image, torch.Tensor):\n",
    "                negative_image = feature_extractor(negative_image, return_tensors=\"pt\").pixel_values\n",
    "            negative_image = negative_image.to(device=device, dtype=dtype)\n",
    "            uncond_image_enc_hidden_states = image_encoder(negative_image, output_hidden_states=True).hidden_states[-2]\n",
    "\n",
    "        uncond_image_enc_hidden_states = uncond_image_enc_hidden_states.repeat_interleave(num_images_per_prompt, dim=0)\n",
    "        return image_enc_hidden_states, uncond_image_enc_hidden_states\n",
    "    else:\n",
    "        image_embeds = image_encoder(image).image_embeds\n",
    "        image_embeds = image_embeds.repeat_interleave(num_images_per_prompt, dim=0)\n",
    "        uncond_image_embeds = torch.zeros_like(image_embeds)\n",
    "\n",
    "        return image_embeds, uncond_image_embeds"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "@torch.no_grad()\n",
    "def prepare_ip_adapter_image_embeds(\n",
    "    unet,\n",
    "    image_encoder,\n",
    "    feature_extractor,\n",
    "    ip_adapter_image,\n",
    "    do_classifier_free_guidance,\n",
    "    device,\n",
    "    num_images_per_prompt,\n",
    "    ip_adapter_negative_image=None,\n",
    "):\n",
    "    if not isinstance(ip_adapter_image, list):\n",
    "        ip_adapter_image = [ip_adapter_image]\n",
    "\n",
    "    if len(ip_adapter_image) != len(unet.encoder_hid_proj.image_projection_layers):\n",
    "        raise ValueError(\n",
    "            f\"`ip_adapter_image` must have same length as the number of IP Adapters. Got {len(ip_adapter_image)} images and {len(unet.encoder_hid_proj.image_projection_layers)} IP Adapters.\"\n",
    "        )\n",
    "\n",
    "    image_embeds = []\n",
    "    for single_ip_adapter_image, image_proj_layer in zip(\n",
    "        ip_adapter_image, unet.encoder_hid_proj.image_projection_layers\n",
    "    ):\n",
    "        output_hidden_state = not isinstance(image_proj_layer, ImageProjection)\n",
    "        single_image_embeds, single_negative_image_embeds = encode_image(\n",
    "            image_encoder,\n",
    "            feature_extractor,\n",
    "            single_ip_adapter_image,\n",
    "            device,\n",
    "            1,\n",
    "            output_hidden_state,\n",
    "            negative_image=ip_adapter_negative_image,\n",
    "        )\n",
    "        single_image_embeds = torch.stack([single_image_embeds] * num_images_per_prompt, dim=0)\n",
    "        single_negative_image_embeds = torch.stack([single_negative_image_embeds] * num_images_per_prompt, dim=0)\n",
    "\n",
    "        if do_classifier_free_guidance:\n",
    "            single_image_embeds = torch.cat([single_negative_image_embeds, single_image_embeds])\n",
    "            single_image_embeds = single_image_embeds.to(device)\n",
    "\n",
    "        image_embeds.append(single_image_embeds)\n",
    "\n",
    "    return image_embeds\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "4bec0e55fd884409a235a267a3787263",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Loading pipeline components...:   0%|          | 0/7 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "bbd2cbc4dc9f4a8bae8f630ead55e2d4",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "  0%|          | 0/25 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "vae = AutoencoderKL.from_pretrained(\n",
    "    \"madebyollin/sdxl-vae-fp16-fix\",\n",
    "    torch_dtype=torch.float16,\n",
    ").to(\"cuda\")\n",
    "\n",
    "pipeline = StableDiffusionXLPipeline.from_pretrained(\n",
    "    \"RunDiffusion/Juggernaut-XL-v9\",\n",
    "    torch_dtype=torch.float16,\n",
    "    vae=vae,\n",
    "    variant=\"fp16\",\n",
    ").to(\"cuda\")\n",
    "\n",
    "pipeline.scheduler = DPMSolverMultistepScheduler.from_config(pipeline.scheduler.config)\n",
    "pipeline.scheduler.config.use_karras_sigmas = True\n",
    "\n",
    "pipeline.load_ip_adapter(\n",
    "    \"h94/IP-Adapter\",\n",
    "    subfolder=\"sdxl_models\",\n",
    "    weight_name=\"ip-adapter-plus_sdxl_vit-h.safetensors\",\n",
    "    image_encoder_folder=\"models/image_encoder\",\n",
    ")\n",
    "pipeline.set_ip_adapter_scale(0.7)\n",
    "\n",
    "ip_image = load_image(\"https://github.com/huggingface/diffusers/assets/5442875/901d8bd8-7a59-4fe7-bda1-a0e0d6c7dffd\")\n",
    "negative_ip_image = load_image(\"https://github.com/huggingface/diffusers/assets/5442875/901d8bd8-7a59-4fe7-bda1-a0e0d6c7dffd\")\n",
    "\n",
    "image_embeds = prepare_ip_adapter_image_embeds(\n",
    "    unet=pipeline.unet,\n",
    "    image_encoder=pipeline.image_encoder,\n",
    "    feature_extractor=pipeline.feature_extractor,\n",
    "    ip_adapter_image=[[ip_image]],\n",
    "    do_classifier_free_guidance=True,\n",
    "    device=\"cuda\",\n",
    "    num_images_per_prompt=1,\n",
    "    ip_adapter_negative_image=negative_ip_image,\n",
    ")\n",
    "\n",
    "\n",
    "prompt = \"cinematic photo of a cyborg in the city, 4k, high quality, intricate, highly detailed\"\n",
    "negative_prompt = \"blurry, smooth, plastic\"\n",
    "\n",
    "image = pipeline(\n",
    "    prompt=prompt,\n",
    "    negative_prompt=negative_prompt,\n",
    "    ip_adapter_image_embeds=image_embeds,\n",
    "    guidance_scale=6.0,\n",
    "    num_inference_steps=25,\n",
    "    generator=torch.Generator(device=\"cpu\").manual_seed(1556265306),\n",
    ").images[0]\n",
    "\n",
    "image.save(\"result.png\")"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
